package patch

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/zhufuyi/sponge/cmd/sponge/commands/generate"
	"github.com/zhufuyi/sponge/pkg/gofile"
	"github.com/zhufuyi/sponge/pkg/replacer"

	"github.com/spf13/cobra"
)

// GenMysqlInitCommand generate mysql initialization code
func GenMysqlInitCommand() *cobra.Command {
	var (
		moduleName string // go.mod module name
		outPath    string // output directory
		targetFile = "internal/model/init.go"
	)

	cmd := &cobra.Command{
		Use:   "gen-mysql-init",
		Short: "Generate mysql initialization code",
		Long: `generate mysql initialization code

Examples:
  # generate mysql initialization code.
  sponge patch gen-mysql-init --module-name=yourModuleName

  # generate mysql initialization code, and specify the server directory, Note: code generation will be canceled when the latest generated file already exists.
  sponge patch gen-mysql-init --out=./yourServerDir
`,
		SilenceErrors: true,
		SilenceUsage:  true,
		RunE: func(cmd *cobra.Command, args []string) error {
			mdName, _ := getNamesFromOutDir(outPath)
			if mdName != "" {
				moduleName = mdName
			} else if moduleName == "" {
				return fmt.Errorf(`required flag(s) "module-name" not set, use "sponge patch gen-mysql-init -h" for help`)
			}

			var isEmpty bool
			if outPath == "" {
				isEmpty = true
			} else {
				isEmpty = false
				if gofile.IsExists(targetFile) {
					fmt.Printf("'%s' already exists, no need to generate it.\n", targetFile)
					return nil
				}
			}

			var err error
			outPath, err = runMysqlCliCommand(moduleName, outPath)
			if err != nil {
				return err
			}

			if isEmpty {
				fmt.Printf(`
using help:
  move the folder "internal" to your project code folder.

`)
			}
			if gofile.IsWindows() {
				targetFile = "\\" + strings.ReplaceAll(targetFile, "/", "\\")
			} else {
				targetFile = "/" + targetFile
			}
			fmt.Printf("generate \"mysql-init\" codes successfully, out = %s\n", outPath+targetFile)
			return nil
		},
	}

	cmd.Flags().StringVarP(&moduleName, "module-name", "m", "", "module-name is the name of the module in the 'go.mod' file")
	cmd.Flags().StringVarP(&outPath, "out", "o", "", "output directory, default is ./mysql-init_<time>, "+
		"if you specify the directory where the web or microservice generated by sponge, the module-name flag can be ignored")

	return cmd
}

func runMysqlCliCommand(moduleName string, outPath string) (string, error) {
	subTplName := "mysql-init"
	r := generate.Replacers[generate.TplNameSponge]
	if r == nil {
		return "", errors.New("replacer is nil")
	}

	// setting up template information
	subDirs := []string{"internal/model"} // only the specified subdirectory is processed, if empty or no subdirectory is specified, it means all files
	ignoreDirs := []string{}              // specify the directory in the subdirectory where processing is ignored
	ignoreFiles := []string{              // specify the files in the subdirectory to be ignored for processing
		"userExample.go", "init_test.go",
	}

	r.SetSubDirsAndFiles(subDirs)
	r.SetIgnoreSubDirs(ignoreDirs...)
	r.SetIgnoreSubFiles(ignoreFiles...)
	fields := addMysqlAndRedisInitFields(moduleName)
	r.SetReplacementFields(fields)
	_ = r.SetOutputDir(outPath, subTplName)
	if err := r.SaveFiles(); err != nil {
		return "", err
	}

	return r.GetOutputDir(), nil
}

func addMysqlAndRedisInitFields(moduleName string) []replacer.Field {
	var fields []replacer.Field

	fields = append(fields, []replacer.Field{
		{
			Old:             "github.com/zhufuyi/sponge/internal",
			New:             moduleName + "/internal",
			IsCaseSensitive: false,
		},
		{
			Old:             "github.com/zhufuyi/sponge/configs",
			New:             moduleName + "/configs",
			IsCaseSensitive: false,
		},
	}...)

	return fields
}

// get moduleName and serverName from directory
func getNamesFromOutDir(dir string) (moduleName string, serverName string) {
	if dir == "" {
		return "", ""
	}
	data, err := os.ReadFile(dir + "/docs/gen.info")
	if err != nil {
		return "", ""
	}

	ms := strings.Split(string(data), ",")
	if len(ms) != 2 {
		return "", ""
	}

	return ms[0], ms[1]
}
